package com.wz.ftpclient;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FtpClient {
	private static Logger log = LoggerFactory.getLogger(FtpClient.class);

    private String host;
    private int port;
    private String username;
    private String password;
    private FTPClient ftp = new FTPClient();
    
    /**
     * @param host     ip地址
     * @param port     端口号
     * @param username 用户名
     * @param password 密码
     */
    public FtpClient(String host, int port, String username, String password) {
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;

        //有必要进行设置
        FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
        conf.setServerLanguageCode("zh");	//解决文件名的中文乱码问题
        ftp.setControlEncoding("GBK");			//编码问题请自己调试一下
        ftp.configure(conf);
    }

    /**
     * 连接ftp服务器
     * @return
     */
    private boolean connectFtp() {
        boolean flag = false;
        int reply;
        try {
            long startLogin = System.currentTimeMillis();
            ftp.connect(host, port);// 连接ftp服务器
            log.info("连接：" + host + ":" + port);
            ftp.login(username, password);// 登录
            log.info("登录:" + username + password);
            reply = ftp.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
                log.info("登陆失败");
                return false;
            }
            flag = true;
            long endLogin = System.currentTimeMillis();
            log.info("登录成功，登录耗时:" + (endLogin - startLogin) + "ms");
            log.info("开启被动模式");
            ftp.enterLocalPassiveMode();
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            ftp.setFileTransferMode(FTP.BINARY_FILE_TYPE);
        } catch (IOException e) {
            log.error("连接异常，请检查地址和端口", e);
        }
        return flag;
    }

    /**
     * 登出
     * @return
     */
    private boolean logOut() {
    	long logOutTime = System.currentTimeMillis();
    	boolean flag = false;
    	try {
    		if (ftp != null) flag = ftp.logout();
    	} catch (IOException e) {
    		log.error("登出异常", e);
    	} finally{
    		try {
				ftp.disconnect();
			} catch (IOException e) {
				log.error("disconnect异常", e);
			}
    	}
    	long endLogOutTime = System.currentTimeMillis();
    	log.info("登出耗时:" + (endLogOutTime - logOutTime) + "ms");
    	return flag;
    }

    /**
     * 上传文件
     * @param path        ftp服务器的文件路径
     * @param filename    保存文件名
     * @param inputStream 文件流
     * @return
     */
    private boolean uploadFile(String path, String filename, InputStream inputStream) {
        boolean flag = false;
        if (connectFtp()) {
            try {
                log.info("Remote system is " + ftp.getSystemType());
                if (!makeDirectory(path)){											//创建路径,你必须先创建/a/b,再创建/a/b/c
                	log.info("创建目录【" + path + "】失败");	
                	return false;
                } else ftp.changeWorkingDirectory(path);
                ftp.enterLocalPassiveMode();
                ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
                flag = ftp.storeFile(filename, inputStream);
                if (flag == true) {
                    log.info("上传成功");
                } else {
                    log.info("上传失败");
                }
            } catch (SocketException e) {
            	log.error("Socket异常", e);
            } catch (IOException e) {
            	log.error("io异常", e);
            } finally {
            	try {
            		if (inputStream != null)
            			inputStream.close();
            	} catch (IOException e) {
            		log.error("io异常", e);
            	}
            	logOut();
            }
        }
        return flag;
    }

    private boolean makeDirectory(String path) throws IOException{
    	String[] paths = getEntirelyPath(path);
    	for (int j = 0; j < paths.length; j++) {
			if (!ftp.changeWorkingDirectory(paths[j])){
				if (!ftp.makeDirectory(paths[j])){
					log.info("创建目录" + paths[j] + "失败");
					return false;
				}
			}
		}
    	return true;
    }

    /**
     * 获得完全的路径集合
     * 比如 参数 path=/a/b/c/d/ 得到 String[] = '/','/a/','/a/b/','/a/b/c/','/a/b/c/d/'
     * @param path
     * @return
     */
    private static String[] getEntirelyPath(String path){
    	String[] pathArray = path.split("/");
    	String[] paths = new String[pathArray.length];
    	int j = 0;
    	StringBuilder p = new StringBuilder();
    	for (int i = 0; i < pathArray.length; i++) {
    		paths[j] = p.append(pathArray[i]).append("/").toString();
    		j++;
		}
    	return paths;
    }

    /**
     * 上传文件
     * @param path       ftp服务器的文件路径
     * @param filename   文件名
     * @param sourcePath 要上传的文件路径
     * @return
     */
    public boolean uploadFile(String path, String filename, String sourcePath) {
        boolean flag = false;
        File file = new File(sourcePath);
        if (file.exists() && file.isFile()) {
            try {
                FileInputStream fis = new FileInputStream(file);
                flag = uploadFile(path, filename, fis);
            } catch (FileNotFoundException e) {
            	log.error("File异常", e);
            }
        } else {
            log.error("文件不存在或者该文件是文件夹");
        }
        return flag;
    }

    /**
     * 
     * @param downloadPath 保存到本地的路径
     * @param remotePath 远程路径
     * @param remoteFilenames 远程文件集合
     * @return
     */
    public boolean downloadFile(String downloadPath, String remotePath, String[] remoteFilenames) {
        boolean downloadFlag = false;
        if (connectFtp()) {
            long startDownloadTime = System.currentTimeMillis();
            try {
                if (ftp.changeWorkingDirectory(remotePath)) {
                    log.info("当前目录:" + ftp.printWorkingDirectory());
                    long singleDownTime = 0;
                    long writeFileTime = 0;
                    long startMatchTime = System.currentTimeMillis();
                    for (String remoteFilename : remoteFilenames) {
                        long startDownloadFile = System.currentTimeMillis();
                        InputStream inputStream = ftp.retrieveFileStream(remoteFilename);
                        long endDownloadFile = System.currentTimeMillis();
                        log.info("下载文件" + remoteFilename + "耗时:" + (endDownloadFile - startDownloadFile) + "ms");
 
                        singleDownTime = endDownloadFile - startDownloadFile;

                        if (inputStream == null) {
                            log.info("无法下载文件:" + remoteFilename);
                            continue;
                        } else {
                            log.info("下载的文件是:" + remoteFilename);
                        }

                        long startWriteFileTolocal = System.currentTimeMillis();
                        OutputStream output = new FileOutputStream(new File(downloadPath + remoteFilename));
                        byte[] bytes = new byte[1024];
                        int i = -1;
                        try {
                            while ((i = inputStream.read(bytes)) != -1) {
                                output.write(bytes, 0, i);
                            }
                            output.flush();
                            downloadFlag = true;
                        } catch (IOException e) {
                        	log.error("IO异常", e);
                        } finally {
                            inputStream.close();
                            ftp.completePendingCommand();
                            output.close();
                        }
                        long endWriteFileTolocal = System.currentTimeMillis();
                        writeFileTime += (endWriteFileTolocal - startWriteFileTolocal);
                    }
                    long endMatchTime = System.currentTimeMillis();
                    log.info("文件匹配耗时:" + (endMatchTime - startMatchTime - singleDownTime - writeFileTime)+ "ms");
                    log.info("写入文件耗时:" + writeFileTime + "ms");
                }
            } catch (IOException e) {
            	log.error("切换工作目录报错!", e);
            }
            long endDownloadTime = System.currentTimeMillis();
            log.info("下载所有文件加匹配耗时:" + (endDownloadTime - startDownloadTime) + "ms");
            logOut();
        }
        return downloadFlag;
    }

    /**
     * 罗列某个路径下的所有文件和子路径
     * @param path
     * @return
     * @throws IOException
     */
    public FTPFile[] listFiles(String path) throws IOException{
    	if (connectFtp()) {
    		try {
    			log.info("Remote system is " + ftp.getSystemType());
    			log.info("查看路径:" + path);
    			return ftp.listFiles(path);
    		} catch (Exception e) {
    			log.error("查询ftp目录异常", e);
    		} finally {
    			logOut();
    		}
    	}
    	return new FTPFile[0];
    }
 
    /**
     * 罗列某两个路径下的所有文件和子路径
     * 可以改造成-不定参类型 String... path
     * @param pathA
     * @param pathB
     * @return
     * @throws IOException
     */
    public FTPFile[] listFiles(String pathA, String pathB) throws IOException{
    	if (connectFtp()) {
    		try {
    			log.info("Remote system is " + ftp.getSystemType());
    			log.info("查看路径:" + pathA);
    			FTPFile[] fileA = ftp.listFiles(pathA);
    			FTPFile[] fileB = ftp.listFiles(pathB);

    			FTPFile[] totalFiles = new FTPFile[fileA.length+fileB.length];

    			System.arraycopy(fileA, 0, totalFiles, 0, fileA.length);
    			System.arraycopy(fileB, 0, totalFiles, fileA.length, fileB.length);
    			return totalFiles;
    		} catch (Exception e) {
    			log.error("查询ftp目录异常", e);
    		} finally {
    			logOut();
    		}
    	}
    	return new FTPFile[0];
    }

    public static void main(String[] args) {
		FtpClient ftpClient = new FtpClient("127.0.0.1", 21, "a", "b");
		//上传
		ftpClient.uploadFile("/a/b/c/d/", "1.txt", "D://tmp//1.txt");
		//下载
		ftpClient.downloadFile("D://tmp//", "/a/b/c/d/", new String[]{"1.txt","2.txt"});
	}
}